const Cesium = require('cesium')
let viewer

class CesiumAlgo {
  constructor(id) {
    if (!id) return
    // 构造函数，初始化viwer对象
    viewer = (() => {
      //墨卡托投影系加载
      const Tms = new Cesium.UrlTemplateImageryProvider({
        url: '../../static/wxmaps/{z}/{x}/{y}.jpg',
        // url: 'http://mt1.google.cn/vt/lyrs=s&hl=zh-CN&x={x}&y={y}&z={z}&s=Gali',
        tilingScheme: new Cesium.WebMercatorTilingScheme(), //解析规则  墨卡托
        maximumLevel: 7 //最大层级
      })
      //初始化视角
      let china = Cesium.Rectangle.fromDegrees(100, -60, 110, 90)
      Cesium.Camera.DEFAULT_VIEW_RECTANGLE = china
      //初始化地球
      let viewer = new Cesium.Viewer(id, {
        imageryProvider: Tms,
        shouldAnimate: false, // 自动播放
        // terrainProvider: terrain,//加载地形
        animation: true, //是否显示动画控制器
        timeline: true, //是否显示时间轴
        baseLayerPicker: false, //是否显示地图底图切换控件  明确如何设置不同地图的访问路径  似乎离线必须要设置成false
        homeButton: true, //是否显示主页控件  默认是true
        navigationHelpButton: false, //是否显示帮助按钮
        geocoder: false, //是否显示地名查找控件
        fullscreenButton: false, //全屏按钮
        vrButton: false, //是否启用地图双屏控件  感觉没用。。。。
        infoBox: true, //是否显示默认气泡窗口控件  默认true
        selectionIndicator: true, //是否显示选中元素标识控件 默认true  通常和上面的infoBox搭配使用
        shadows: false
        //terrainShadows:RECEIVE_ONLY,
        //        skyBox:new Cesium.SkyBox({
        //            source:{
        //                positiveX:"../images/skyBox/tycho2t3_80_px.jpg",
        //                negativeX:"../images/skyBox/tycho2t3_80_mx.jpg",
        //                positiveY:"../images/skyBox/tycho2t3_80_py.jpg",
        //                negativeY:"../images/skyBox/tycho2t3_80_my.jpg",
        //            }
        //        })
      })
      //去掉版权
      viewer._cesiumWidget._creditContainer.style.display = 'none'
      // 控制视角不转到地下
      viewer.scene.globe.depthTestAgainstTerrain = true
      // 隐藏 animation 和 timeline
      viewer.animation.container.style.visibility = 'hidden'
      viewer.timeline.container.style.visibility = 'hidden'
      //启用地球照明   打开昼夜分界线
      // viewer.scene.globe.enableLighting = true;
      //启用雾
      // viewer.scene.fog.enabled = true;
      // viewer.scene.skyAtmosphere = true;
      //开启模型检查器
      // viewer.extend(Cesium.viewerCesiumInspectorMixin);

      let date = new Date() //可返回格林威治时间和本地时间之间的时差
      let h = 0 - date.getTimezoneOffset()
      viewer.animation.viewModel.timeFormatter = function(date, viewModel) {
        //重新构造一个当前系统时间的儒略日
        let dateZone = Cesium.JulianDate.addMinutes(
          date,
          h,
          new Cesium.JulianDate()
        )
        let gregorianDate = Cesium.JulianDate.toGregorianDate(dateZone)
        let millisecond = Math.round(gregorianDate.millisecond) //倍速是否小于1，小于1则显示毫秒级
        if (Math.abs(viewModel._clockViewModel.multiplier) < 1) {
          return Cesium.sprintf(
            '%02d:%02d:%02d.%03d',
            gregorianDate.hour,
            gregorianDate.minute,
            gregorianDate.second,
            gregorianDate.millisecond
          )
        }
        return Cesium.sprintf(
          '%02d:%02d:%02d',
          gregorianDate.hour,
          gregorianDate.minute,
          gregorianDate.second
        )
      }
      //设置日期
      viewer.animation.viewModel.dateFormatter = function(date, viewModel) {
        let dateZone = Cesium.JulianDate.addMinutes(
          date,
          h,
          new Cesium.JulianDate()
        )
        let gregorianDate = Cesium.JulianDate.toGregorianDate(dateZone) //设置格式为xx/xx/xx，格式可自定义为任意你想要的
        return Cesium.sprintf(
          '%4d-%02d-%02d',
          gregorianDate.year,
          gregorianDate.month,
          gregorianDate.day
        )
      }

      return viewer
    })()
  }

  /**
   * 点
   * @param {} id       点编号
   * @param {} name     点名称
   * @param {} color    颜色
   * @param {} size     点大小
   * @param {} lon      经度
   * @param {} lat      纬度
   */
  renderPoint(id, name, color, size, lon, lat) {
    viewer.entities.add({
      id,
      name,
      position: Cesium.Cartesian3.fromDegrees(lon, lat),
      point: {
        pixelSize: size,
        color: Cesium.Color.fromCssColorString(color)
      },
      label: {
        text: name,
        font: '12px sans-serif',
        pixelOffset: new Cesium.Cartesian2(0, -20), //y大小根据行数和字体大小改变
        horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
        backgroundPadding: new Cesium.Cartesian2(5, 5),
        showBackground: true,
        backgroundColor: Cesium.Color.fromCssColorString('rgba(15, 44, 88, .8)')
      }
    })
  }

  /**
   * 线
   * @param {} id         线编号
   * @param {} name       线名称
   * @param {} color      颜色
   * @param {} width      点大小
   * @param {} position    位置 经度，纬度，经度，纬度
   */
  renderLine(id, name, color, width, position) {
    viewer.entities.add({
      id,
      name,
      polyline: {
        positions: Cesium.Cartesian3.fromDegreesArray(position),
        width,
        material: Cesium.Color.fromCssColorString(color)
      }
    })
  }

  /**
   * 流动纹理线
   * @param {} id         线编号
   * @param {} name       线名称
   * @param {} color      颜色
   * @param {} width      点大小
   * @param {} position    位置 经度，纬度，经度，纬度
   */
  renderFlowLine(canvas, id, name, width, position) {
    //通过3个画布交替切换实现纹理动态
    let changenum = 3
    let curCanvas = 'a'
    canvas.map(item => {
      let canvas = document.getElementById(item[0])
      let cwidth = 400
      let cheight = 40
      let ctx = canvas.getContext('2d')
      ctx.clearRect(0, 0, cwidth, cheight)
      ctx.fillStyle = 'rgba(255, 255, 255, 0)'
      ctx.fillRect(0, 0, cwidth, cheight)

      for (let ii = 0; item[1] <= 400; ii++) {
        ctx.lineWidth = 5
        //开始一个新的绘制路径
        ctx.beginPath()
        //设置弧线的颜色
        ctx.strokeStyle = 'rgba(250, 74, 68, 1)'
        //以canvas中左上角画一根长400px的直线
        ctx.moveTo(item[1], 0)
        ctx.lineTo(item[1] + 10, 20)
        ctx.lineTo(item[1], 40)
        //按照指定的路径绘制
        ctx.stroke()
        item[1] += 15
      }
    })

    viewer.entities.add({
      id,
      name,
      polyline: {
        positions: Cesium.Cartesian3.fromDegreesArrayHeights(position),
        width,
        material: new Cesium.ImageMaterialProperty({
          image: new Cesium.CallbackProperty(() => {
            changenum++
            let canvas = document.getElementById('canvas-' + curCanvas)
            if (changenum >= 3) {
              changenum = 0
              if (curCanvas === '3') curCanvas = '2'
              else if (curCanvas === '2') curCanvas = '1'
              else curCanvas = '3'
            }
            return canvas
          }, false),
          transparent: true
        }),
        arcType: Cesium.ArcType.NONE
      }
    })
  }

  /**
   * 箭头曲线
   * @param {} id         线编号
   * @param {} name       线名称
   * @param {} color      颜色
   * @param {} width      点大小
   * @param {} position    位置 经度，纬度，经度，纬度
   */
  renderArrowLine(id, name, color, width, position) {
    viewer.entities.add({
      id,
      name,
      polyline: {
        positions: Cesium.Cartesian3.fromDegreesArrayHeights(position),
        width,
        material: new Cesium.PolylineArrowMaterialProperty(
          Cesium.Color.fromCssColorString(color)
        )
        // clampToGround: true
      }
    })
  }

  /**
   * 卫星轨道
   * @param {} id         轨道编号
   * @param {} name       轨道名称
   * @param {} color      颜色
   * @param {} width      点大小
   * @param {} positon    位置 经度，纬度，经度，纬度
   */
  renderSatOrbital(id, name, color, width, position) {
    viewer.entities.add({
      id,
      name,
      polyline: {
        positions: Cesium.Cartesian3.fromDegreesArrayHeights(position),
        width,
        // arcType: Cesium.ArcType.RHUMB,
        material: Cesium.Color.fromCssColorString(color)
      }
    })
  }

  /**
   * 添加模型
   * @param {} id         卫星id
   * @param {} name       卫星名称
   * @param {} lon        模型经度
   * @param {} lat        模型纬度
   * @param {} height     模型高度
   * @param {} uri        模型路径
   * @param {} heading    模型偏航角
   * @param {} pitch      模型俯仰角
   * @param {} roll       模型滚动角
   * @param {} minSize    模型最小尺寸
   */
  renderModel(
    id,
    name,
    lon,
    lat,
    height,
    uri,
    heading = 0,
    pitch = 90,
    roll = 0,
    minSize = 150
  ) {
    let position = Cesium.Cartesian3.fromDegrees(lon, lat, height), //设置位置
      headingNum = Cesium.Math.toRadians(heading), //设置偏航角
      pitchNum = Cesium.Math.toRadians(pitch), //设置俯仰角
      rollNum = roll, //设置滚动角
      hpr = new Cesium.HeadingPitchRoll(headingNum, pitchNum, rollNum), //生成四元数
      orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr) //设置姿态
    viewer.entities.add({
      id,
      name,
      position: position,
      orientation,
      model: {
        uri,
        minimumPixelSize: minSize
        // maximumScale: 20000
      }
    })
  }

  /**
   * 添加卫星模型（模型节点控制动画）
   * @param {} id         卫星id
   * @param {} name       卫星名称
   * @param {} lon        模型经度
   * @param {} lat        模型纬度
   * @param {} height     模型高度
   * @param {} url        模型路径
   * @param {} node       模型节点名称
   * @param {} heading    模型偏航角
   * @param {} pitch      模型俯仰角
   * @param {} roll       模型滚动角
   * @param {} minSize    模型最小尺寸
   */
  renderSatModel(id, lon, lat, height, url, node, minSize = 150) {
    let quat = Cesium.Quaternion.fromAxisAngle(
      Cesium.Cartesian3.UNIT_Y,
      Cesium.Math.toRadians(90)
    ) //Cesium.Cartesian3.UNIT_Z初始化（0.0,1.0,0.0）
    let mat3 = Cesium.Matrix3.fromQuaternion(quat)
    let mat4 = Cesium.Matrix4.fromRotationTranslation(
      mat3,
      Cesium.Cartesian3.ZERO
    ) //Cesium.Cartesian3.ZERO 初始化（0.0,0.0,0.0）
    let modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(
      Cesium.Cartesian3.fromDegrees(lon, lat, height)
    )
    let m = Cesium.Matrix4.multiplyTransformation(
      modelMatrix,
      mat4,
      new Cesium.Matrix4()
    )
    let modelss = viewer.scene.primitives.add(
      Cesium.Model.fromGltf({
        id,
        url,
        modelMatrix: m,
        scale: 1,
        minimumPixelSize: minSize
      })
    )
    // 卫星太阳板动画
    let nodeName = node
    Cesium.when(modelss.readyPromise).then(function(modelss) {
      // 初始角度
      let i = 60
      let flag = false
      setInterval(() => {
        if (i === 60) {
          flag = true
        } else if (i === -60) {
          flag = false
        }
        if (flag) {
          i -= 2
        } else {
          i += 2
        }
        nodeName.map(item => {
          let node = modelss.getNode(item)
          let quat = Cesium.Quaternion.fromAxisAngle(
            Cesium.Cartesian3.UNIT_Y,
            Cesium.Math.toRadians(i)
          ) //Cesium.Cartesian3.UNIT_Z初始化（0.0,1.0,0.0）
          let pt = Cesium.Quaternion.fromAxisAngle(
            Cesium.Cartesian3.UNIT_Z,
            Cesium.Math.toRadians(90)
          ) //Cesium.Cartesian3.UNIT_Z初始化（0.0,1.0,0.0）
          let mat3 = Cesium.Matrix3.fromQuaternion(quat)
          node.matrix = Cesium.Matrix4.fromRotationTranslation(
            mat3,
            pt,
            node.matrix
          )
        })
      }, 50)
    })
  }

  /**
   * 视角锁定
   * @param {} id  模型 id
   */
  flyToTarget(id) {
    let targetEntity = viewer.entities.getById(id)
    let position = targetEntity.position
    let ellipsoid = viewer.scene.globe.ellipsoid
    let cartesian3 = new Cesium.Cartesian3(
      position._value.x,
      position._value.y,
      position._value.z
    )
    let cartographic = ellipsoid.cartesianToCartographic(cartesian3)
    let lon = Cesium.Math.toDegrees(cartographic.longitude)
    let lat = Cesium.Math.toDegrees(cartographic.latitude)
    let alt = cartographic.height
    // 选择当前 entity
    viewer.selectedEntity = targetEntity
    viewer.camera.flyTo({
      destination: Cesium.Cartesian3.fromDegrees(lon, lat + 1, alt + 10000000),
      orientation: {
        heading: Cesium.Math.toRadians(0),
        pitch: Cesium.Math.toRadians(-90),
        roll: 0
      },
      // 动画持续时间
      duration: 1.5
    })
    // 初始化视角跟踪
    viewer.trackedEntity = viewer
    // 设置视角跟踪
    viewer.trackedEntity = targetEntity
  }

  /**
   * 卫星波束
   * @param {} canvas     材质 canvas 及 圆半径 ["canvas-a", 5], ["canvas-b", 10], ["canvas-c", 15]
   * @param {} id         编号
   * @param {} name       名称
   * @param {} lon        经度
   * @param {} lat        纬度
   * @param {} height     高度
   */
  renderSatBeam(canvas, id, name, lon, lat, height) {
    //通过3个画布交替切换实现探测纹理动态
    let changenum = 0
    let curCanvas = 'a'
    canvas.map(item => {
      let canvas = document.getElementById(item[0])
      let cwidth = 400
      let cheight = 400
      let ctx = canvas.getContext('2d')
      ctx.clearRect(0, 0, cwidth, cheight)
      ctx.fillStyle = 'rgba(255, 255, 255, 0)'
      ctx.fillRect(0, 0, cwidth, cheight)

      for (let ii = 0; item[1] <= 200; ii++) {
        ctx.lineWidth = 3
        //开始一个新的绘制路径
        ctx.beginPath()
        //设置弧线的颜色
        let trans = 1.0 - item[1] / 255
        ctx.strokeStyle = 'rgba(250, 74, 68, ' + trans + ')'
        let circle = {
          x: 200, //圆心的x轴坐标值
          y: 200, //圆心的y轴坐标值
          r: item[1] //圆的半径
        }
        //以canvas中的坐标点(200,200)为圆心，绘制一个半径为50px的圆形
        ctx.arc(circle.x, circle.y, circle.r, 0, Math.PI * 2, true)
        //按照指定的路径绘制弧线
        ctx.stroke()
        item[1] += 15
      }
    })

    // 创建圆锥实体
    viewer.entities.add({
      id,
      name,
      position: Cesium.Cartesian3.fromDegrees(lon, lat, height),
      cylinder: {
        length: 2000000,
        topRadius: 0.0,
        bottomRadius: 500000.0,
        //topSurface: false,    //新增参数，控制顶部是否渲染
        // bottomSurface: false,    //新增参数，控制底部是否渲染
        material: new Cesium.ImageMaterialProperty({
          image: new Cesium.CallbackProperty(() => {
            changenum++
            let canvas = document.getElementById('canvas-' + curCanvas)
            if (changenum >= 5) {
              changenum = 0
              if (curCanvas === 'a') curCanvas = 'b'
              else if (curCanvas === 'b') curCanvas = 'c'
              else curCanvas = 'a'
            }
            return canvas
          }, false),
          transparent: true
        })
      }
    })
  }

  /**
   * 卫星轨道动画
   * @param {} data       [[秒, 经度, 纬度, 高度], [秒, 经度, 纬度, 高度]]
   * @param {} id         模型编号
   * @param {} timeRate   播放速度
   */
  sateAnimation(data, id, timeRate) {
    let eTime = data
    // 开始时间
    let start = Cesium.JulianDate.fromDate(new Date())
    // 结束时间
    let stop = Cesium.JulianDate.addSeconds(
      start,
      3600,
      new Cesium.JulianDate()
    )

    // 设置开始时间轴
    viewer.clock.startTime = start
    // 设置当前时间轴
    viewer.clock.currentTime = start
    // 设置结束时间轴
    viewer.clock.stopTime = stop
    // 时间速率
    viewer.clock.multiplier = timeRate
    // 时间轴
    viewer.timeline.zoomTo(start, stop)
    // 循环执行 到达终点时间 重新从起点时间开始
    viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP
    let property = new Cesium.SampledPositionProperty()
    for (let i = 0; i < data.length; i++) {
      let time = Cesium.JulianDate.addSeconds(
        start,
        data[i][0],
        new Cesium.JulianDate()
      )
      let position = Cesium.Cartesian3.fromDegrees(
        data[i][1],
        data[i][2],
        data[i][3]
      )
      //添加位置，和时间对应
      property.addSample(time, position)
    }
    let model = viewer.entities.getById(id)
    // 关联时间轴
    model.availability = new Cesium.TimeIntervalCollection([
      new Cesium.TimeInterval({
        start,
        stop
      })
    ])
    model.position = property
    // console.log(model.position);
    // model.orientation = new Cesium.VelocityOrientationProperty(property);
    // console.log(model.orientation);
  }

  /**
   * 创建时间控件
   * @param {} startTime      开始时间
   * @param {} endTime        结束时间
   */
  renderTimeControls(startTime, stopTime) {
    let clock,
      animationContainer = document.getElementsByClassName('animation')[0],
      timelineContainer = document.getElementsByClassName('time-line')[0]
    clock = new Cesium.Clock({
      startTime: Cesium.JulianDate.fromIso8601(startTime),
      currentTime: Cesium.JulianDate.fromIso8601(stopTime),
      stopTime: Cesium.JulianDate.fromIso8601(stopTime),
      clockRange: Cesium.ClockRange.LOOP_STOP,
      clockStep: Cesium.ClockStep.SYSTEM_CLOCK_MULTIPLIER
    })
    //时间轴默认开始
    clock.shouldAnimate = true
    //创建ViewModel
    let clockViewModel = new Cesium.ClockViewModel(clock)
    let animation = new Cesium.Animation(
      animationContainer,
      new Cesium.AnimationViewModel(clockViewModel)
    )
    let timeline = new Cesium.Timeline(timelineContainer, clock)
  }

  /**
   * 加载 3D Tiles 数据
   * @param {} url    路径
   */
  render3DTiles(url) {
    // 加载数据
    let city = viewer.scene.primitives.add(
      new Cesium.Cesium3DTileset({
        url
      })
    )
    // 调整 3dTiles 样式
    // 根据高度的不同，指定不同的颜色、
    let heightStyle = new Cesium.Cesium3DTileStyle({
      color: {
        conditions: [
          ['${height} >= 300', 'rgba(45, 0, 75, 0.5)'],
          ['${height} >= 200', 'rgb(102, 71, 151)'],
          ['${height} >= 100', 'rgb(170, 162, 204)'],
          ['${height} >= 50', 'rgb(224, 226, 238)'],
          ['${height} >= 25', 'rgb(252, 230, 200)'],
          ['${height} >= 10', 'rgb(248, 176, 87)'],
          ['${height} >= 5', 'rgb(198, 106, 11)'],
          ['true', 'rgb(127, 59, 8)']
        ]
      }
    })

    // 指定 city 样式
    city.style = heightStyle
  }

  /**
   * 加载 geojson 数据
   * @param {} url    路径
   */
  renderGeoJson(viewer, url) {
    let geojsonOptions = {
      // 贴地
      clampToGround: true
    }
    let neighborhoodsPromise = Cesium.GeoJsonDataSource.load(
      url,
      geojsonOptions
    )

    neighborhoodsPromise.then(function(dataSource) {
      // 把数据加载大搜场景里
      viewer.dataSources.add(dataSource)
      // 遍历数据
      let neighborhoodEntities = dataSource.entities.values
      for (let i = 0; i < neighborhoodEntities.length; i++) {
        let entity = neighborhoodEntities[i]

        if (Cesium.defined(entity.polygon)) {
          // 每个区块的名字
          entity.name = entity.properties.neighborhood
          // 每个多边形的材质  随机颜色
          entity.polygon.material = Cesium.Color.fromRandom({
            red: 0.1,
            maximumGreen: 0.5,
            minimumBlue: 0.5,
            alpha: 0.3
          })
          // 地理区块贴在地面之上
          entity.polygon.classificationType = Cesium.ClassificationType.TERRAIN
          // 从多边形里取出顶点
          let polyPositions = entity.polygon.hierarchy.getValue(
            Cesium.JulianDate.now()
          ).positions
          // 通过顶点构建包围球 取出中心点
          let polyCenter = Cesium.BoundingSphere.fromPoints(polyPositions)
            .center
          // 包围球中心点做贴地偏移
          polyCenter = Cesium.Ellipsoid.WGS84.scaleToGeodeticSurface(polyCenter)
          // 赋值
          entity.position = polyCenter
          // 创建标签，标签在中心点显示
          entity.label = {
            text: entity.name,
            showBackground: true,
            scale: 0.6,
            horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
            verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
            distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
              10.0,
              8000.0
            ),
            disableDepthTestDistance: 100.0
          }
        }
      }
    })
  }

  /**
   * 圆形扩散扫描
   * @param {} viewer       viewer 对象
   * @param {} lon          经度
   * @param {} lat          纬度
   * @param {} r            最大半径
   * @param {} color        颜色
   * @param {} duration     持续时间
   */
  renderCircleScan(viewer, lon, lat, r, color, duration) {
    // 防止移动、放大缩小会视觉偏移depthTestAgainstTerrain
    // 设置该属性为true之后，标绘将位于地形的顶部；如果设为false（默认值），那么标绘将位于平面上。
    // 缺陷：开启该属性有可能在切换图层时会引发标绘消失的bug。
    viewer.scene.globe.depthTestAgainstTerrain = true
    //中心位子
    let cartographicCenter = new Cesium.Cartographic(
      Cesium.Math.toRadians(lon),
      Cesium.Math.toRadians(lat),
      0
    )
    // 扫描颜色
    let scanColor = new Cesium.Color.fromCssColorString(color)

    let ScanSegmentShader =
      'uniform sampler2D colorTexture;\n' +
      'uniform sampler2D depthTexture;\n' +
      'varying vec2 v_textureCoordinates;\n' +
      'uniform vec4 u_scanCenterEC;\n' +
      'uniform vec3 u_scanPlaneNormalEC;\n' +
      'uniform float u_radius;\n' +
      'uniform vec4 u_scanColor;\n' +
      'vec4 toEye(in vec2 uv, in float depth)\n' +
      ' {\n' +
      ' vec2 xy = vec2((uv.x * 2.0 - 1.0),(uv.y * 2.0 - 1.0));\n' +
      ' vec4 posInCamera =czm_inverseProjection * vec4(xy, depth, 1.0);\n' +
      ' posInCamera =posInCamera / posInCamera.w;\n' +
      ' return posInCamera;\n' +
      ' }\n' +
      'vec3 pointProjectOnPlane(in vec3 planeNormal, in vec3 planeOrigin, in vec3 point)\n' +
      '{\n' +
      'vec3 v01 = point -planeOrigin;\n' +
      'float d = dot(planeNormal, v01) ;\n' +
      'return (point - planeNormal * d);\n' +
      '}\n' +
      'float getDepth(in vec4 depth)\n' +
      '{\n' +
      'float z_window = czm_unpackDepth(depth);\n' +
      'z_window = czm_reverseLogDepth(z_window);\n' +
      'float n_range = czm_depthRange.near;\n' +
      'float f_range = czm_depthRange.far;\n' +
      'return (2.0 * z_window - n_range - f_range) / (f_range - n_range);\n' +
      '}\n' +
      'void main()\n' +
      '{\n' +
      'gl_FragColor = texture2D(colorTexture, v_textureCoordinates);\n' +
      'float depth = getDepth( texture2D(depthTexture, v_textureCoordinates));\n' +
      'vec4 viewPos = toEye(v_textureCoordinates, depth);\n' +
      'vec3 prjOnPlane = pointProjectOnPlane(u_scanPlaneNormalEC.xyz, u_scanCenterEC.xyz, viewPos.xyz);\n' +
      'float dis = length(prjOnPlane.xyz - u_scanCenterEC.xyz);\n' +
      'if(dis < u_radius)\n' +
      '{\n' +
      'float f = 1.0 -abs(u_radius - dis) / u_radius;\n' +
      'f = pow(f, 4.0);\n' +
      'gl_FragColor = mix(gl_FragColor, u_scanColor, f);\n' +
      '}\n' +
      '}\n'

    let _Cartesian3Center = Cesium.Cartographic.toCartesian(cartographicCenter)
    let _Cartesian4Center = new Cesium.Cartesian4(
      _Cartesian3Center.x,
      _Cartesian3Center.y,
      _Cartesian3Center.z,
      1
    )
    let _CartographicCenter1 = new Cesium.Cartographic(
      cartographicCenter.longitude,
      cartographicCenter.latitude,
      cartographicCenter.height + 500
    )
    let _Cartesian3Center1 = Cesium.Cartographic.toCartesian(
      _CartographicCenter1
    )
    let _Cartesian4Center1 = new Cesium.Cartesian4(
      _Cartesian3Center1.x,
      _Cartesian3Center1.y,
      _Cartesian3Center1.z,
      1
    )
    let _time = new Date().getTime()
    let _scratchCartesian4Center = new Cesium.Cartesian4()
    let _scratchCartesian4Center1 = new Cesium.Cartesian4()
    let _scratchCartesian3Normal = new Cesium.Cartesian3()
    let ScanPostStage = new Cesium.PostProcessStage({
      fragmentShader: ScanSegmentShader,
      uniforms: {
        u_scanCenterEC: function() {
          return Cesium.Matrix4.multiplyByVector(
            viewer.camera._viewMatrix,
            _Cartesian4Center,
            _scratchCartesian4Center
          )
        },
        u_scanPlaneNormalEC: function() {
          let temp = Cesium.Matrix4.multiplyByVector(
            viewer.camera._viewMatrix,
            _Cartesian4Center,
            _scratchCartesian4Center
          )
          let temp1 = Cesium.Matrix4.multiplyByVector(
            viewer.camera._viewMatrix,
            _Cartesian4Center1,
            _scratchCartesian4Center1
          )
          _scratchCartesian3Normal.x = temp1.x - temp.x
          _scratchCartesian3Normal.y = temp1.y - temp.y
          _scratchCartesian3Normal.z = temp1.z - temp.z
          Cesium.Cartesian3.normalize(
            _scratchCartesian3Normal,
            _scratchCartesian3Normal
          )
          return _scratchCartesian3Normal
        },
        u_radius: function() {
          return (r * ((new Date().getTime() - _time) % duration)) / duration
        },
        u_scanColor: scanColor
      }
    })
    viewer.scene.postProcessStages.add(ScanPostStage)
    return ScanPostStage
  }

  /**
   * 添加雷达扫描
   * @param {} viewer       viewer 对象
   * @param {} lon          经度
   * @param {} lat          纬度
   * @param {} r            最大半径
   * @param {} color        颜色
   * @param {} duration     持续时间
   */
  renderRadarScan(viewer, lon, lat, r, color, duration) {
    // 防止移动、放大缩小会视觉偏移depthTestAgainstTerrain
    // 设置该属性为true之后，标绘将位于地形的顶部；如果设为false（默认值），那么标绘将位于平面上。
    // 缺陷：开启该属性有可能在切换图层时会引发标绘消失的bug。
    viewer.scene.globe.depthTestAgainstTerrain = true
    //中心位子
    let cartographicCenter = new Cesium.Cartographic(
      Cesium.Math.toRadians(lon),
      Cesium.Math.toRadians(lat),
      0
    )
    // 扫描颜色
    let scanColor = new Cesium.Color.fromCssColorString(color)

    let ScanSegmentShader =
      'uniform sampler2D colorTexture;\n' +
      'uniform sampler2D depthTexture;\n' +
      'varying vec2 v_textureCoordinates;\n' +
      'uniform vec4 u_scanCenterEC;\n' +
      'uniform vec3 u_scanPlaneNormalEC;\n' +
      'uniform vec3 u_scanLineNormalEC;\n' +
      'uniform float u_radius;\n' +
      'uniform vec4 u_scanColor;\n' +
      'vec4 toEye(in vec2 uv, in float depth)\n' +
      ' {\n' +
      ' vec2 xy = vec2((uv.x * 2.0 - 1.0),(uv.y * 2.0 - 1.0));\n' +
      ' vec4 posInCamera =czm_inverseProjection * vec4(xy, depth, 1.0);\n' +
      ' posInCamera =posInCamera / posInCamera.w;\n' +
      ' return posInCamera;\n' +
      ' }\n' +
      'bool isPointOnLineRight(in vec3 ptOnLine, in vec3 lineNormal, in vec3 testPt)\n' +
      '{\n' +
      'vec3 v01 = testPt - ptOnLine;\n' +
      'normalize(v01);\n' +
      'vec3 temp = cross(v01, lineNormal);\n' +
      'float d = dot(temp, u_scanPlaneNormalEC);\n' +
      'return d > 0.5;\n' +
      '}\n' +
      'vec3 pointProjectOnPlane(in vec3 planeNormal, in vec3 planeOrigin, in vec3 point)\n' +
      '{\n' +
      'vec3 v01 = point -planeOrigin;\n' +
      'float d = dot(planeNormal, v01) ;\n' +
      'return (point - planeNormal * d);\n' +
      '}\n' +
      'float distancePointToLine(in vec3 ptOnLine, in vec3 lineNormal, in vec3 testPt)\n' +
      '{\n' +
      'vec3 tempPt = pointProjectOnPlane(lineNormal, ptOnLine, testPt);\n' +
      'return length(tempPt - ptOnLine);\n' +
      '}\n' +
      'float getDepth(in vec4 depth)\n' +
      '{\n' +
      'float z_window = czm_unpackDepth(depth);\n' +
      'z_window = czm_reverseLogDepth(z_window);\n' +
      'float n_range = czm_depthRange.near;\n' +
      'float f_range = czm_depthRange.far;\n' +
      'return (2.0 * z_window - n_range - f_range) / (f_range - n_range);\n' +
      '}\n' +
      'void main()\n' +
      '{\n' +
      'gl_FragColor = texture2D(colorTexture, v_textureCoordinates);\n' +
      'float depth = getDepth( texture2D(depthTexture, v_textureCoordinates));\n' +
      'vec4 viewPos = toEye(v_textureCoordinates, depth);\n' +
      'vec3 prjOnPlane = pointProjectOnPlane(u_scanPlaneNormalEC.xyz, u_scanCenterEC.xyz, viewPos.xyz);\n' +
      'float dis = length(prjOnPlane.xyz - u_scanCenterEC.xyz);\n' +
      'float twou_radius = u_radius * 2.0;\n' +
      'if(dis < u_radius)\n' +
      '{\n' +
      'float f0 = 1.0 -abs(u_radius - dis) / u_radius;\n' +
      'f0 = pow(f0, 64.0);\n' +
      'vec3 lineEndPt = vec3(u_scanCenterEC.xyz) + u_scanLineNormalEC * u_radius;\n' +
      'float f = 0.0;\n' +
      'if(isPointOnLineRight(u_scanCenterEC.xyz, u_scanLineNormalEC.xyz, prjOnPlane.xyz))\n' +
      '{\n' +
      'float dis1= length(prjOnPlane.xyz - lineEndPt);\n' +
      'f = abs(twou_radius -dis1) / twou_radius;\n' +
      'f = pow(f, 3.0);\n' +
      '}\n' +
      'gl_FragColor = mix(gl_FragColor, u_scanColor, f + f0);\n' +
      '}\n' +
      '}\n'

    let _Cartesian3Center = Cesium.Cartographic.toCartesian(cartographicCenter)
    let _Cartesian4Center = new Cesium.Cartesian4(
      _Cartesian3Center.x,
      _Cartesian3Center.y,
      _Cartesian3Center.z,
      1
    )
    let _CartographicCenter1 = new Cesium.Cartographic(
      cartographicCenter.longitude,
      cartographicCenter.latitude,
      cartographicCenter.height + 500
    )
    let _Cartesian3Center1 = Cesium.Cartographic.toCartesian(
      _CartographicCenter1
    )
    let _Cartesian4Center1 = new Cesium.Cartesian4(
      _Cartesian3Center1.x,
      _Cartesian3Center1.y,
      _Cartesian3Center1.z,
      1
    )
    let _CartographicCenter2 = new Cesium.Cartographic(
      cartographicCenter.longitude + Cesium.Math.toRadians(0.001),
      cartographicCenter.latitude,
      cartographicCenter.height
    )
    let _Cartesian3Center2 = Cesium.Cartographic.toCartesian(
      _CartographicCenter2
    )
    let _Cartesian4Center2 = new Cesium.Cartesian4(
      _Cartesian3Center2.x,
      _Cartesian3Center2.y,
      _Cartesian3Center2.z,
      1
    )
    let _RotateQ = new Cesium.Quaternion()
    let _RotateM = new Cesium.Matrix3()
    let _time = new Date().getTime()
    let _scratchCartesian4Center = new Cesium.Cartesian4()
    let _scratchCartesian4Center1 = new Cesium.Cartesian4()
    let _scratchCartesian4Center2 = new Cesium.Cartesian4()
    let _scratchCartesian3Normal = new Cesium.Cartesian3()
    let _scratchCartesian3Normal1 = new Cesium.Cartesian3()
    let ScanPostStage = new Cesium.PostProcessStage({
      fragmentShader: ScanSegmentShader,
      uniforms: {
        u_scanCenterEC: function() {
          return Cesium.Matrix4.multiplyByVector(
            viewer.camera._viewMatrix,
            _Cartesian4Center,
            _scratchCartesian4Center
          )
        },
        u_scanPlaneNormalEC: function() {
          let temp = Cesium.Matrix4.multiplyByVector(
            viewer.camera._viewMatrix,
            _Cartesian4Center,
            _scratchCartesian4Center
          )
          let temp1 = Cesium.Matrix4.multiplyByVector(
            viewer.camera._viewMatrix,
            _Cartesian4Center1,
            _scratchCartesian4Center1
          )
          _scratchCartesian3Normal.x = temp1.x - temp.x
          _scratchCartesian3Normal.y = temp1.y - temp.y
          _scratchCartesian3Normal.z = temp1.z - temp.z
          Cesium.Cartesian3.normalize(
            _scratchCartesian3Normal,
            _scratchCartesian3Normal
          )
          return _scratchCartesian3Normal
        },
        u_radius: r,
        u_scanLineNormalEC: function() {
          let temp = Cesium.Matrix4.multiplyByVector(
            viewer.camera._viewMatrix,
            _Cartesian4Center,
            _scratchCartesian4Center
          )
          let temp1 = Cesium.Matrix4.multiplyByVector(
            viewer.camera._viewMatrix,
            _Cartesian4Center1,
            _scratchCartesian4Center1
          )
          let temp2 = Cesium.Matrix4.multiplyByVector(
            viewer.camera._viewMatrix,
            _Cartesian4Center2,
            _scratchCartesian4Center2
          )
          _scratchCartesian3Normal.x = temp1.x - temp.x
          _scratchCartesian3Normal.y = temp1.y - temp.y
          _scratchCartesian3Normal.z = temp1.z - temp.z
          Cesium.Cartesian3.normalize(
            _scratchCartesian3Normal,
            _scratchCartesian3Normal
          )
          _scratchCartesian3Normal1.x = temp2.x - temp.x
          _scratchCartesian3Normal1.y = temp2.y - temp.y
          _scratchCartesian3Normal1.z = temp2.z - temp.z
          let tempTime = ((new Date().getTime() - _time) % duration) / duration
          Cesium.Quaternion.fromAxisAngle(
            _scratchCartesian3Normal,
            tempTime * Cesium.Math.PI * 2,
            _RotateQ
          )
          Cesium.Matrix3.fromQuaternion(_RotateQ, _RotateM)
          Cesium.Matrix3.multiplyByVector(
            _RotateM,
            _scratchCartesian3Normal1,
            _scratchCartesian3Normal1
          )
          Cesium.Cartesian3.normalize(
            _scratchCartesian3Normal1,
            _scratchCartesian3Normal1
          )
          return _scratchCartesian3Normal1
        },
        u_scanColor: scanColor
      }
    })
    viewer.scene.postProcessStages.add(ScanPostStage)
    return ScanPostStage
  }
  /*
   * 视图切换至纽约
   */
  flyToNewYork() {
    viewer.camera.flyTo({
      destination: new Cesium.Cartesian3(
        1333013.3830867545,
        -4658437.313384082,
        4136389.9800129677
      ),
      orientation: {
        heading: 0.48098280328767906,
        pitch: -0.40641901480345144,
        roll: 0
      },
      // 动画持续时间
      duration: 1.5
    })
  }

  /**
   * 卫星动画跟踪
   */
  flyToSat(id) {
    let targetEntity = viewer.entities.getById(id)
    // viewer.camera.flyTo({
    //   orientation: {
    //     heading: 0.48098280328767906,
    //     pitch: -0.40641901480345144,
    //     roll: 0,
    //   },
    //   // 动画持续时间
    //   duration: 1.5
    // });
    // 初始化视角跟踪
    viewer.trackedEntity = viewer
    // 设置视角跟踪
    viewer.trackedEntity = targetEntity
  }
}

export { CesiumAlgo, viewer }
